#include "common.h"

#include "counter.h"
#include "Vdispatch_test.h"

#define TRACE 0
#define MAX_WAVE 1000000
#define MAX_CYCLE 1000000
Vdispatch_test *top;
VerilatedFstC *tfp;
static uint64_t wave_cnt = 0;

#define DISPATCH_WIDTH 3
Counter cycle("cycle");

bool percent(int change)
{
    uint32_t r = rand();
    r %= 100;
    return r < change;
}
uint32_t rand_n(uint32_t n)
{
    uint32_t r = rand();
    return r % n;
}
void dump()
{
#if TRACE
    if (wave_cnt < MAX_WAVE)
    {
        tfp->dump(Verilated::time());
        wave_cnt++;
    }
#endif
}
void run()
{
    top->eval();
    dump();
    Verilated::timeInc(1);

    top->clk = 0;
    top->eval();
    dump();
    Verilated::timeInc(2);

    top->clk = 1;
    top->eval();
    dump();
    Verilated::timeInc(1);
}
#define FU_NUM 5
const char fu_table[FU_NUM] = {
    0b1,
    0b10,
    0b100,
    0b1000,
    0b10000};
void clear_all()
{
    top->i_valid = 0;
    top->o_rob_write_ready = 0;
    top->o_alu_ready = 0;
    top->o_bru_ready = 0;
    top->o_lsu_ready = 0;
    top->o_mdu_ready = 0;
    top->o_misc_ready = 0;
    for (size_t i = 0; i < DISPATCH_WIDTH; i++)
    {
        top->i_rop_valid[i] = 0;
    }
}
void reset()
{
    clear_all();
    top->rst = 1;
    run();
    run();
    run();
    top->rst = 0;
}
void make_rand_input()
{
    // LOG("make_rand_input");
    static word_t pc = 0x80000000;
    static uint8_t rob_idx = 0;

    uint32_t r = rand_n(DISPATCH_WIDTH + 1);
    for (size_t i = 0; i < r; i++)
    {
        top->i_rop_valid[i] = 1;
        top->i_rop_fu_type[i] = fu_table[rand_n(FU_NUM)];
        top->i_rop_pc[i] = pc;
        top->o_rob_write_rob_idx[i] = rob_idx;
        pc += 4;
        rob_idx++;
    }
    top->i_valid = 1;
}
void clear_all_fired()
{
    if (top->i_valid & top->i_ready)
    {
        top->i_valid = 0;
        for (size_t i = 0; i < DISPATCH_WIDTH; i++)
        {
            top->i_rop_valid[i] = 0;
        }
    }
    if (top->o_rob_write_valid & top->o_rob_write_ready)
    {
        top->o_rob_write_ready = 0;
    }
}

void dispatch_rand_test()
{
    reset();
    do
    {
        run();
        cycle++;
        clear_all_fired();
        if (!top->i_valid && percent(70))
        {
            make_rand_input();
        }
        top->o_alu_ready = percent(90);
        top->o_bru_ready = percent(90);
        top->o_lsu_ready = percent(80);
        top->o_mdu_ready = percent(95);
        top->o_misc_ready = percent(95);
        top->o_rob_write_ready = percent(95);
    } while ((!Verilated::gotFinish()) && cycle < MAX_CYCLE);
}
int main()
{
    srand(0);
    Verilated::randSeed(0);
    Verilated::randReset(1);
    top = new Vdispatch_test();
#if TRACE
    Verilated::traceEverOn(true);
    tfp = new VerilatedFstC();
    top->trace(tfp, 0);
    tfp->open("Vdispatch_test.fst");
#endif
    reset();
    // Run all tests
    dispatch_rand_test();
    // Final model cleanup
    Verilated::timeInc(2);
    dump();
    top->final();
#if TRACE
    tfp->close();
    delete tfp;
#endif
    delete top;
    Counter::print_counters();
    LOG("All test done");
    return 0;
}